#ifndef __OURHDR_H
#define __OURHDR_H

#include <cstdio>
#include <iostream>
#include <array>
#include <list>
#include <vector>

using std::array;
using std::vector;
using std::list;
using std::cout;
using std::cerr;
using std::cin;
using std::endl;

enum Direction { LEFT, RIGHT, UP, DOWN };

Direction opposite(Direction);

inline bool isOpposite(Direction left, Direction right){
	return	(left == LEFT && right == RIGHT) ||
			(left == RIGHT && right == LEFT) ||
			(left == UP && right == DOWN) ||
			(left == DOWN && right == UP);
}

class Pos{
public:
	static constexpr unsigned	max_row = 30;
	static constexpr unsigned	max_col = 50;

	Pos(unsigned row = 0, unsigned col = 0);
	Pos(const Pos &);
	Pos& operator=(const Pos &pos);

	unsigned row() const { return m_row; }
	unsigned col() const { return m_col; }
	bool	nextTo(const Pos &another) const;
	bool	isAboveOf(const Pos &another) const;
	bool	isBelowOf(const Pos &another) const;
	bool	isOnTheLeftOf(const Pos &another) const;
	bool	isOnTheRightOf(const Pos &another) const;

	Pos		upper() const { return Pos((m_row+max_row-1)%max_row,m_col); }
	Pos		lower() const { return Pos((m_row+1)%max_row,m_col); }
	Pos		left() const  { return Pos(m_row,(m_col+max_col-1)%max_col); }
	Pos		right() const { return Pos(m_row,(m_col+1)%max_col); }
	Pos		toDirect(Direction direction) const;
	
	bool	setRow(unsigned);
	bool	setCol(unsigned);
private:
	unsigned	m_row;
	unsigned	m_col;
};

inline bool operator==(const Pos& left, const Pos& right){
	return (left.row() == right.row() && left.col() == right.col());
}

Direction operator-(const Pos& left, const Pos& right);

inline Pos::Pos(unsigned row, unsigned col)
	: m_row(row) , m_col(col)
{
	if(row >= max_row || col >= max_col)
		fprintf(stderr, "create an invalid pos");
}

inline Pos::Pos(const Pos &pos)
	:m_row(pos.m_row), m_col(pos.m_col)
{}

inline Pos Pos::toDirect(Direction direction) const{
	switch(direction){
		case UP:
			return upper();
		case DOWN:
			return lower();
		case LEFT:
			return left();
		case RIGHT:
			return right();
		default:
			cerr << "incorrect direction";
	}
}
inline bool Pos::setRow(unsigned row){
	if(row >= max_row){
		return false;
	}
	else{
		m_row = row;
		return true;
	}
}

inline bool Pos::setCol(unsigned col){
	if(col >= max_col){
		return false;
	}
	else {
		m_col = col;
		return true;
	}
}

class Pixel{
public:
	static const unsigned		max_row = 30;
	static const unsigned		max_col = 50;

	Pixel(unsigned row = 0, unsigned col = 0, char c = ' ');
	Pixel(const Pos &pos, char c);
	Pixel(const Pixel &p);
	Pixel& operator=(const Pixel &p);
	~Pixel();

	Pos pos() const;
	unsigned row() const;
	unsigned col() const;
	char character() const;
	void setRow(unsigned r);
	void setCol(unsigned c);
	void setPos(const Pos &p);
	void setPos(Pos *p);
	void setCharacter(char c);
private:
	Pos			*m_pos;
	char		m_character;
};

inline Pixel::Pixel(unsigned row, unsigned col, char c)
	: m_pos(new Pos(row,col)), m_character(c)
{}

inline Pixel::Pixel(const Pos &pos, char c)
	: m_pos(new Pos(pos)), m_character(c)
{}

inline Pixel::Pixel(const Pixel &p){
	m_pos = new Pos(*(p.m_pos));
	m_character = p.m_character;
}

inline Pixel& Pixel::operator=(const Pixel &p){
	m_pos = new Pos(*(p.m_pos));
	m_character = p.m_character;
	return *this;
}

inline Pixel::~Pixel(){
	delete m_pos;
}

inline Pos Pixel::pos() const{
	return *m_pos;
}

inline unsigned Pixel::row() const{
	return m_pos->row();
}

inline unsigned Pixel::col() const{
	return m_pos->col();
}

inline char Pixel::character() const{
	return m_character;
}

inline void Pixel::setPos(const Pos &p){
	setRow(p.row());
	setCol(p.col());
}

inline void Pixel::setRow(unsigned r){
	m_pos->setRow(r);
}

inline void Pixel::setCol(unsigned c){
	m_pos->setCol(c);
}

inline void Pixel::setPos(Pos *p){
	m_pos = p;
}

inline void Pixel::setCharacter(char c){
	m_character = c;
}


inline bool operator==(const Pixel& left, const Pixel& right){
	return (left.row() == right.row() && left.col() == right.col() && left.character() == right.character());
}

inline std::ostream& operator<<(std::ostream& os, const Pixel& p){
	os << "(" << p.row() << ", " << p.col() << ", " << p.character() << ")";
}




class Screen;
class Object{
public:
	Object() = default;

	Pixel& operator[](std::size_t n);

	int count() const;
	bool hasPos(unsigned row, unsigned col) const;
	bool hasPos(const Pos &) const;
	Pixel first() const { return m_pixels.front(); }
	void castTo(Screen& screen) const;
	bool overlapWith(const Object &obj) const;

	void add(const Pixel &p){ addBack(p);}
	void addBack(const Pixel&);
	void addFront(const Pixel&);
	bool del(const Pixel &p);
	void delBack();
	void delFront();

	
private:
	std::list<Pixel> m_pixels;
};

class Screen{
public:
	Screen();

	void setPixel(unsigned, unsigned, char);
	char getPixel(unsigned, unsigned) const;

	void setWords(int line, const char* str);
	void setInfo(const char* str);

	void clear();
	void show() const;
private:
	char m_pixels[Pixel::max_row+4][Pixel::max_col+2];
};

class Snake : public Object{
public:
	Snake();

	void move(const Pos&, Direction);
	void eat(const Pixel&, Direction);
	void eat(const Pos&, Direction);
	Pixel head() const;
	Direction direction() const { return m_direction; }
	void setDirection(Direction d) { m_direction = d; }

private:
	Snake(const Snake&);

	Direction m_direction;
};

class Food: public Object{
public:
	Food();

	int value() const { return m_value; }

	bool setValue(int value) { m_value = value; }
	void regen(const vector<Pos> &poses);
private:

	int m_value;
};

class Wall: public Object{
public:
	Wall(unsigned int stageNum = 1): Wall(stages[stageNum]) {}

private:
	static constexpr unsigned wordlength = 8 * sizeof(unsigned);
	static constexpr unsigned words = (Pos::max_col + wordlength - 1) / wordlength;
	typedef unsigned Map[Pos::max_row][words];
	Map stages[2] ={ 
	{
		{0xffffffff, 0x0003ffff},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
	   	{0x00000001, 0x00020000},
		{0xffffffff, 0x0003ffff}
	}, {
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0xc00fffff, 0x0003ffff},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0xc00fffff, 0x0003ffff},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000},
		{0x00000000, 0x00000000}
	}

	};

	Wall(const Map &map);

};

class Game{
public:
	Game();
	~Game();

	void exec();
private:
	int welcome();

	Screen	*m_screen;
	Snake	*m_snake;
	Food	*m_food;
	Wall	*m_wall;
	unsigned stride;	/* Time(ms) of per stride */
	unsigned m_score;
};

#endif /* __OURHDR_H */
